﻿using Foxit.PDF;
using Foxit.PDF.Imaging;
using Foxit.PDF.PageElements;
using Sustainalytics.Entities.RiskRatings;
using Sustainalytics.Reporting.Framework;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sustainalytics.RiskRatings.Reporting
{
    internal class EventsSection : PdfReportSectionGenerator
    {
        private readonly float columnWidth;

        private readonly float XRightColumn;

        public EventsSection(int index) : base(index)
        {
            this._reportSection.Name = "Events section";
            columnWidth = (_report.PageMaxWidth - (float)HorisontalSpacing.Large) / 2;
            XRightColumn = _report.PageMaxWidth / 2 + (float)HorisontalSpacing.Large;
        }

        private RiskRatingMaterialEsgIssueBase CurrentIssue { get; set; }

        private PdfReportPage CurrentPage { get; set; }

        private float CurrentXOffset { get; set; }

        private float CurrentYOffset { get; set; }

        public override void ComposeSectionContent()
        {
            _report.PageSize = PageSize.A4;
            _report.PageOrientation = PageOrientation.Portrait;
            _report.PageMargins = 25f;
            _report.PageDimensions = new PageDimensions(_report.PageSize, _report.PageOrientation, _report.PageMargins);

            if (_dataSource.CompanyProfile.MaterialEsgIssues.Any())
            {
                foreach (var issue in _dataSource.CompanyProfile.MaterialEsgIssues.Where(x => x.Type == MaterialIssueType.MaterialIssue))
                {
                    var indicatorsCode = issue.Indicators.Where(x => x.IndicatorType == IndicatorType.EventIndicator).Select(x => x.Code);
                    var eventIndicators = _dataSource.Events.Where(x => indicatorsCode.Contains(x.CanvasCode));

                    if (eventIndicators == null || !eventIndicators.Any())
                    {
                        continue;
                    }
                    CurrentIssue = issue;
                    

                    foreach (var eventIndicator in eventIndicators)
                    {
                        AddNewPage();
                        var associatedIncidents = _dataSource.Incident.Where(x => x.EventIndicatorId == eventIndicator.Id);

                        AddEventsFirstPage(eventIndicator, associatedIncidents, CurrentXOffset, CurrentYOffset);

                        CurrentXOffset = XRightColumn;
                        foreach (var incidentGroup in associatedIncidents.GroupBy(x => x.IncidentId))
                        {
                            AddIncindetGroup(incidentGroup, CurrentXOffset, CurrentYOffset);
                        }
                    }
                }
            }
        }

        private float AddAnalystCommentary(EventIndicator issue, float x, float y)
        {
            float currentYOffset = y;
            if (issue == null || string.IsNullOrEmpty(issue.ControversySummary))
                return currentYOffset;

            CurrentPage.Elements.Add(SectionTile("Assessment", x, currentYOffset));

            currentYOffset += (float)TextSizes.SubSectionTitle + (float)VerticalSpacing.Small;

            FormattedTextArea textArea = new FormattedTextArea(issue.ControversySummary, x, currentYOffset, columnWidth, 500, Fonts.RobotoFontFamily, 7, false);
            CurrentPage.Elements.Add(textArea);

            currentYOffset += textArea.GetRequiredHeight() + (float)VerticalSpacing.Small;

            textArea = new FormattedTextArea(issue.EventJustification ?? string.Empty, x, currentYOffset, columnWidth, 500, Fonts.RobotoFontFamily, 7, false);
            CurrentPage.Elements.Add(textArea);

            currentYOffset += textArea.GetRequiredHeight();

            return currentYOffset;
        }

        private float AddEventHeader(EventIndicator issue, float x, float currentYOffset)
        {
            CurrentPage.Elements.Add(new Label("EVENT DETAILS", x, currentYOffset, columnWidth, (float)TextSizes.SubSectionTitle, Fonts.MontserratRegular, (float)TextSizes.SubSectionTitle, TextAlign.Left, RgbColor.Black));

            currentYOffset += 20;
            CurrentPage.Elements.Add(new Label(issue.Name + " Event", x, currentYOffset, columnWidth, (float)TextSizes.RegularTextSubtitle, Fonts.MontserratBold, (float)TextSizes.SectionTitle, TextAlign.Left, RgbColor.Black));

            currentYOffset += (float)TextSizes.SectionTitle + (float)VerticalSpacing.Small;
            return currentYOffset;
        }

        private void AddEventsFirstPage(EventIndicator eventIndicator, IEnumerable<Incident> incidents, float x, float y)
        {
            float currentYOffset = AddEventHeader(eventIndicator, x, y);

            float outlookOffset = AddOutlook(eventIndicator, x, currentYOffset);

            var analystOffset = AddAnalystCommentary(eventIndicator, XRightColumn, currentYOffset);

            currentYOffset = Math.Max(outlookOffset, analystOffset) + (float)VerticalSpacing.Large;

            CurrentPage.Elements.Add(new Line(0, currentYOffset, _report.PageMaxWidth, currentYOffset, 1, Colors.SectionColor));
            currentYOffset += (float)VerticalSpacing.Small;

            AddIncidentsMap(incidents.ToList(), x, currentYOffset);
        }

        private Tuple<float, Group> AddIncident(float x, float currentYOffset, Incident incident, string IncidentName = null)
        {
            var group = new Group();
            group.Add(new Label(
                IncidentName ?? incident.Name,
                x,
                currentYOffset,
                columnWidth,
                (float)TextSizes.RegularText,
                Fonts.MontserratRegular,
                (float)TextSizes.RegularText,
                TextAlign.Left,
                RgbColor.Black));
            currentYOffset += (float)TextSizes.RegularText + 2;

            group.Add(new Label(
                $"{ incident.Source } - { incident.IncidentDate.ToString("d MMMM yyyy") }",
                x,
                currentYOffset,
                columnWidth,
                (float)TextSizes.RegularText,
                Fonts.MontserratBold,
                (float)TextSizes.RegularText,
                TextAlign.Left,
                RgbColor.Black));
            currentYOffset += (float)TextSizes.RegularText + (float)VerticalSpacing.Small;
            return new Tuple<float, Group>(currentYOffset, group);
        }

        private void AddIncidentsMap(List<Incident> incidents, float x, float currentYOffset)
        {
            if (!incidents.Any())
            {
                return;
            }
            CurrentPage.Elements.Add(SectionTile("Incident History", x, currentYOffset));

            currentYOffset += (float)TextSizes.SubSectionTitle + (float)VerticalSpacing.Small;

            CurrentYOffset = currentYOffset;

            var map = new Image(
                ImageData.GetImage(
                    IncidentsMap.GetMapImageOptimized(
                        incidents.Select(l => l.Location).ToList(),
                        (int)columnWidth,
                        (int)columnWidth,
                        MapSize.Big)
                    ),
                x,
                currentYOffset - columnWidth / 5,
                0.98f);

            CurrentPage.AddElement(map);

            currentYOffset += columnWidth / 2 + (float)VerticalSpacing.Small;

            HashSet<string> locationSet = new HashSet<string>();
            HashSet<string> tagSet = new HashSet<string>();
            incidents.ForEach(i =>
            {
                if (!locationSet.Contains(i.Location.Address)) { locationSet.Add(i.Location.Address); }
                if (!tagSet.Contains(i.MainTag)) { tagSet.Add(i.MainTag); }
            });

            if (locationSet.Any())
            {
                var locations = locationSet.Aggregate((a, b) => a + ", " + b);
                var textArea = new FormattedTextArea("Locations:" + locations, x, currentYOffset, columnWidth, 500, Fonts.RobotoFontFamily, (float)TextSizes.RegularText, false);
                CurrentPage.Elements.Add(textArea);
                currentYOffset += textArea.GetRequiredHeight() + (float)VerticalSpacing.Small;
            }

            if (tagSet.Any())
            {
                var tags = tagSet.Aggregate((a, b) => a + ", " + b);
                var textArea = new FormattedTextArea("Tags:" + tags, x, currentYOffset, columnWidth, 500, Fonts.RobotoFontFamily, (float)TextSizes.RegularText, false);
                CurrentPage.Elements.Add(textArea);
                currentYOffset += textArea.GetRequiredHeight() + (float)VerticalSpacing.Small;
            }
        }

        private void AddIncindetGroup(IGrouping<string, Incident> incidentGroup, float x, float y)
        {
            float currentYOffset = y;
            float currentXOffset = x;
            var incidents = incidentGroup.OrderByDescending(i => i.IncidentDate).ToArray();
            Tuple<float, Group> item;
            Incident mainIncident = incidents.FirstOrDefault(i => i.IsMain);

            if (mainIncident != null)
            {
                item = AddIncident(currentXOffset, currentYOffset, mainIncident);
                var incidentHeight = Math.Abs(currentYOffset - item.Item1);

                if (CheckNewPageOrNewSection(item.Item1 + incidentHeight))
                {
                    currentXOffset = CurrentXOffset;
                    currentYOffset = CurrentYOffset;
                    item = AddIncident(currentXOffset, currentYOffset, mainIncident);
                    currentYOffset = CurrentYOffset = item.Item1;
                    CurrentPage.Elements.Add(item.Item2);
                }
                else
                {
                    currentYOffset = CurrentYOffset = item.Item1;
                    currentXOffset = CurrentXOffset;
                    CurrentPage.Elements.Add(item.Item2);
                }
            }

            foreach (var incident in incidents.Where(i=>!i.IsMain))
            {
                if (incident != null)
                {
                    item = AddIncident(currentXOffset + 20, currentYOffset, incident, $"Update: {incident.Name}");

                    if (CheckNewPageOrNewSection(item.Item1))
                    {
                        currentXOffset = CurrentXOffset;
                        currentYOffset = CurrentYOffset;
                        item = AddIncident(currentXOffset, currentYOffset, incident);
                        currentYOffset = item.Item1;
                        CurrentPage.Elements.Add(item.Item2);
                        item = AddIncident(currentXOffset + 20, currentYOffset, incident, $"Update: {incident.Name}");
                    }

                    currentYOffset = CurrentYOffset = item.Item1;
                    currentXOffset = CurrentXOffset;
                    CurrentPage.Elements.Add(item.Item2);
                }
            }

            CurrentYOffset += (float)VerticalSpacing.Small;
        }

        private void AddNewPage()
        {
            CurrentPage = new PdfReportPage_MaterialIssues(_report, _dataSource, CurrentIssue, ReportHelper.GetMeiImage(CurrentIssue.ExternalId.ToString(), MeiImageColor.Colored, MeiImageSize.Large));
            _report.Pages.Add(CurrentPage);
            CurrentYOffset = CurrentPage.Top + (float)VerticalSpacing.Small;
            CurrentXOffset = 0;
        }

        private float AddOutlook(EventIndicator issue, float x, float currentYOffset)
        {
            var elements = new Group();

            var image = new Image(ImageData.GetImage(ReportHelper.GetEventImage(issue.EventCategory)), x, currentYOffset, 0.33f);
            elements.Add(image);
            var currentX = x + image.Width;
            var currentY = currentYOffset + (image.Height - (float)TextSizes.SubSectionTitle) / 2;

            elements.Add(new Label(issue.EventCategoryText, currentX, currentY, columnWidth / 2, (float)TextSizes.SubSectionTitle, Fonts.MontserratRegular, (float)TextSizes.SubSectionTitle, TextAlign.Left, RgbColor.Black));

            currentX = columnWidth / 2;

            var arroImage = new Image(ImageData.GetImage(ReportHelper.GetOutlookImage(issue.OutlookLabel ?? string.Empty)), currentX, currentYOffset, 0.33f);

            elements.Add(arroImage);

            currentYOffset += image.Height;
            elements.Add(new Label("Outlook", currentX, currentYOffset, columnWidth, (float)TextSizes.SubSectionTitle, Fonts.MontserratRegular, (float)TextSizes.SubSectionTitle, TextAlign.Left, RgbColor.Black));

            currentX = arroImage.Width + arroImage.X;

            elements.Add(new Label(issue.OutlookLabel ?? string.Empty, currentX, currentY, columnWidth / 2, (float)TextSizes.SubSectionTitle, Fonts.MontserratRegular, (float)TextSizes.SubSectionTitle, TextAlign.Left, RgbColor.Black));

            elements.Add(new Label("Event Category", x, currentYOffset, columnWidth, (float)TextSizes.SubSectionTitle, Fonts.MontserratRegular, (float)TextSizes.SubSectionTitle, TextAlign.Left, RgbColor.Black));

            currentYOffset += (float)TextSizes.SubSectionTitle + (float)VerticalSpacing.Small;

            CurrentPage.Elements.Add(elements);

            if (!string.IsNullOrEmpty(issue.OutlookText))
            {
                CurrentPage.Elements.Add(SectionTile("Outlook", x, currentYOffset));

                currentYOffset += (float)TextSizes.SubSectionTitle + (float)VerticalSpacing.Small;

                FormattedTextArea textArea = new FormattedTextArea(issue.OutlookText, x, currentYOffset, columnWidth, 500, Fonts.RobotoFontFamily, (float)TextSizes.RegularTextSubtitle, false);
                CurrentPage.Elements.Add(textArea);

                currentYOffset += textArea.GetRequiredHeight() + (float)VerticalSpacing.Small;

                issue.OutlookComments?.ForEach(comment =>
                {
                    textArea = new FormattedTextArea(comment, x, currentYOffset, columnWidth, 500, Fonts.RobotoFontFamily, (float)TextSizes.RegularText, false);
                    CurrentPage.Elements.Add(textArea);
                    currentYOffset += textArea.GetRequiredHeight() + (float)VerticalSpacing.Small;
                });
            }

            return currentYOffset;
        }

        private bool CheckNewPageOrNewSection(float offset)
        {
            CurrentYOffset = offset;
            if (offset >= CurrentPage.Dimensions.Height - CurrentPage.Top)
            {
                if (CurrentXOffset >= XRightColumn)
                {
                    AddNewPage();
                    return true;
                }
                CurrentXOffset = XRightColumn; CurrentYOffset = CurrentPage.Top + (float)VerticalSpacing.Small;
                return true;
            }
            return false;
        }

        private Label SectionTile(string title, float x, float y) => new Label(
                    title,
            x,
            y,
            _report.PageMaxWidth,
            (float)TextSizes.SubSectionTitle,
            Fonts.MontserratRegular,
            (float)TextSizes.SubSectionTitle,
            TextAlign.Left,
            RgbColor.Black);
    }
}